﻿using Infrastructure.ElasticSearch.Enums;
using Nest;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Infrastructure.ElasticSearch.Nests
{
    public class ElasticSearchItemRangeProperty<T> : ElasticSearchItem<T> where T : class
    {
        private ElasticSearchRange currRange;
        public ElasticSearchRange CompareRange { get; private set; }

        public ElasticSearchRange CurrentRange { get { return currRange; } }

        public ElasticSearchItemRangeProperty(RangeFieldAttribute rangeAttribute,
           Expression<Func<T, object>> field,
           QueryOperator opera = QueryOperator.Include,
           QueryPositionEnum position = QueryPositionEnum.Must,
           string path = "",
           QueryPositionEnum nestPosition = QueryPositionEnum.None) : base(field, opera, position, path, nestPosition)
        {
            //构造查询field

            SearchFieldName = SearchFieldPre + rangeAttribute.RangeName;
            this.Field = new Nest.Field(SearchFieldName);

            currRange = new ElasticSearchRange(SearchFieldName, rangeAttribute.RangeType, rangeAttribute.RangeOpera);
        }

        public override void SetItemVal(List<string> val)
        {
            base.SetItemVal(val);
            if (val != null && val.Count > 0)
                currRange.SetVal(val[0]);

        }
        public override QueryContainerDescriptor<T> Search()
        {
            QueryContainerDescriptor<T> container = new QueryContainerDescriptor<T>();

            if (currRange.RangeType == RangeTypeEnum.Date)
            {
                var dateRangeQuery = new DateRangeQueryDescriptor<T>();
                GetDateRangeQuery(dateRangeQuery, currRange.RangeOpera, currRange.Val);

                dateRangeQuery.Field(Field);

                if (CompareRange != null && CompareRange.RangeOpera != RangeOperEnum.Non && !string.IsNullOrEmpty(CompareRange.Val))
                    GetDateRangeQuery(dateRangeQuery, CompareRange.RangeOpera, CompareRange.Val);

                container.DateRange(d => dateRangeQuery);
            }
            else if (currRange.RangeType == RangeTypeEnum.Num)
            {
                var numRangeQuery = new NumericRangeQueryDescriptor<T>();

                double.TryParse(currRange.Val.ToString(), out double val1);

                GetNumRangeQuery(numRangeQuery, currRange.RangeOpera, val1);

                numRangeQuery.Field(Field);

                if (CompareRange != null && CompareRange.RangeOpera != RangeOperEnum.Non)
                {
                    double.TryParse(CompareRange.Val.ToString(), out double val2);
                    GetNumRangeQuery(numRangeQuery, CompareRange.RangeOpera, val2);
                }

                container.Range(d => numRangeQuery);
            }
            return container;
        }

        /// <summary>
        /// 设置比较字段
        /// </summary>
        /// <param name="compareField"></param>
        public void SetCompareRange(ElasticSearchItemRangeProperty<T> compareField)
        {
            CompareRange = compareField.CurrentRange;
            CompareRange.SetVal(compareField.ItemListVal[0]);
        }

        #region range比较器

        /// <summary>
        /// 获取时间比较器
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="rangeQuery"></param>
        /// <param name="oper"></param>
        /// <param name="val"></param>
        private static void GetDateRangeQuery(DateRangeQueryDescriptor<T> rangeQuery, RangeOperEnum oper, string val)
        {
            switch (oper)
            {
                case RangeOperEnum.gt:
                    rangeQuery.GreaterThan(DateMath.FromString(val));
                    break;
                case RangeOperEnum.gte:
                    rangeQuery.GreaterThanOrEquals(DateMath.FromString(val));
                    break;
                case RangeOperEnum.lt:
                    rangeQuery.LessThan(DateMath.FromString(val));
                    break;
                case RangeOperEnum.lte:
                    rangeQuery.LessThanOrEquals(DateMath.FromString(val));
                    break;

                default:
                    break;
            }
        }

        /// <summary>
        /// 获取double比较器
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="rangeQuery"></param>
        /// <param name="oper"></param>
        /// <param name="val"></param>
        private static void GetNumRangeQuery(NumericRangeQueryDescriptor<T> rangeQuery, RangeOperEnum oper, double val)
        {
            switch (oper)
            {
                case RangeOperEnum.gt:
                    rangeQuery.GreaterThan(val);
                    break;
                case RangeOperEnum.gte:
                    rangeQuery.GreaterThanOrEquals(val);
                    break;
                case RangeOperEnum.lt:
                    rangeQuery.LessThan(val);
                    break;
                case RangeOperEnum.lte:
                    rangeQuery.LessThanOrEquals(val);
                    break;

                default:
                    break;
            }
        }

        #endregion
    }
}
